import UiPanel from "../UiPanel";
import BaseCustomEvent from "./BaseCustomEvent";
import BasePrefabMgrByHall from "./BasePrefabMgrByHall";

const { ccclass, property } = cc._decorator;
/** 对于game hall 的基础控件类  需要保持和 BaseComponent.ts 同步
 * 
 * @author david
*/
@ccclass
export default class BaseComponentByHall extends cc.Component {

    // @property
    // _MsgFun =null;
    /**
     * 播放按钮点击音效
     */
    playBtnSound() {
        cc.vv.audioUtil.playSound("Hall/sound/button_onclicked");
    }


    /**
     * 传入方法名,使得cc.vv.eventCustom.emit时 可以通过事件调用此方法
     * @param CallBackName 方法名
     */
    onEvent(CallBackName) {
        this["_" + CallBackName] = cc.vv.eventCustom.on(
            "" + CallBackName,
            this[CallBackName].bind(this),
            this.node
        );
    }

    /**
     * 
     * @param CallBackName 方法名
     */
    offEvent(CallBackName) {
        if (typeof this["_" + CallBackName] != "undefined") {
            cc.vv.eventCustom.off("" + CallBackName, this["_" + CallBackName]);
            this["_" + CallBackName] = undefined;
        }
    }


    /**
     * 传入方法名,使得BaseCustomEvent.emit时 可以通过事件调用此方法
     * @param CallBackName 方法名
     */
    onBaseEvent(CallBackName) {
        if (!this[CallBackName]) {
            cc.error("注册事件失败,没有这个方法", arguments)
            return;
        }
        BaseCustomEvent.on(
            "" + CallBackName,
            this[CallBackName].bind(this),
            this.node
        );
    }

    /**
     * 
     * @param CallBackName 方法名
     */
    offBaseEvent(CallBackName) {
        BaseCustomEvent.off("" + CallBackName, this.node);
    }


    /**
     * 设置节点的Active属性,只对不同的进行设置
     * @param node 节点
     * @param flag active值
     */
    setActive(node: cc.Node, flag: boolean = false) {
        if (node) {
            if (node.active != flag) {
                node.active = flag;
            }
        } else {
            cc.error("Node 错误", node, this);
        }
    }

    /**
      * 创建结点
      * @param prefab  
      * @param root 根节点 默认为this.node
      * @param position 初始位置
      * @returns Script 返回预制体所挂同名脚本
      */
    createNode(prefab: cc.Prefab, root: cc.Node = this.node, position?: cc.Vec2) {
        if (!prefab || typeof prefab !== "object") {
            return null;
        }

        let node = cc.instantiate(prefab);
        if (position) {
            node.position = position;
        }

        //已经被关闭了
        if (!this.node || !root) {
            cc.warn("主节点已经被销毁,Name = ", node.name, "加载取消")
            return null;
        }

        node.parent = root;
        return node.getComponent(node.name);
    }

    /** 创建多个节点
     * 
     * @param childrenNames 
     */
    createNodeFactory(childrenNames: string[] = []) {
        childrenNames.map(name => this.createNodeFast(name))
    }

    /** 快速创建一个节点
     * 
     * @param name 
     * 
     * 
     * 
        "@propertyRPS": {
            "prefix": "@property R P S",
            "scope": "javascript,typescript",
            "body": [
                "@property(cc.Node)",
                "${1:name}_Root: cc.Node = null;",
                "@property(cc.Prefab)",
                "${1:name}_Prefab: cc.Prefab = null;",
                "_${1:name}_sc: ${1:name} = null;",
            ],
            "description": "自定义 节点组片段"
        },用于此方式定义的组
     * 
     * 
     */
    createNodeFast(name) {
        this["_" + name + "_sc"] = this.createNode(this[name + "_Prefab"], this[name + "_Root"]);
    }

    /**
     * 改变节点的父节点 
     * 主要用于层级冲突时,调用了外部层级,这里做更换父节点以及坐标的换算
     * 
     * 注意,必须在节点适配结束后调用
     * 注意,此方法没有处理缩放
     * 注意,子节点widget(如果有)必须为once,否则适配错误
     * 
     * @param child 需要更换父节点的节点
     * @param newParent 新的父节点
     * @param oldParent 原父节点
     */
    changeParent(child: cc.Node, newParent: cc.Node, oldParent?: cc.Node) {
        if (!oldParent) {
            oldParent = child.parent;
            if (!oldParent) {
                cc.error("oldParent is null");
                return;
            }
        }
        if (!newParent) {
            cc.error("newParent is null!")
            return;
        }

        let newPos = newParent.convertToNodeSpaceAR(oldParent.convertToWorldSpaceAR(child.position));

        child.parent = newParent;
        child.position = newPos;
    }


    /**
     * 
     * @param path 资源路径
     * @param cb 回调
     * @param type 类型 默认预制体
     */
    loadRes(path: string, cb: Function, type = cc.Prefab) {
        BasePrefabMgrByHall.loadRes(path, cb, type);
    }
    /**
     * 
     *  !!!注意!!!:主预制体释放时需要调用releaseAll(),或者与releaseRes()对应调用
     * 
     * @param path 资源路径
     * @param cb returns Script 返回预制体所挂同名脚本
     * @param root 根节点 默认为this.node
     * @param position 初始位置
     * @param onlyOne 是否一个父节点只有一个子节点(即在一个父节点下预制体只会创建一个)(未實現)
     *  
     */
    loadAndCreatePrefab(path: string, root?: cc.Node, cb?: (sc, /**是否取消加载 */isCancel?: boolean) => void, position?: cc.Vec2, onlyOne?: false) {

        BasePrefabMgrByHall.loadRes(path, (_prefab) => {
            //已经被关闭了
            if (!cc.isValid(this.node) || !cc.isValid(root)) {
                cc.warn("主节点已经被销毁,Name = ", _prefab.data.name, "加载取消")
                // if (cb) {
                //     cb(null, true);
                // }
                return;
            }
            let sc = this.createNode(_prefab, root, position);
            if (cb && cc.isValid(this.node) && cc.isValid(root)) {
                cb(sc);
            }
        })

    }
    /** 异步动态读取预制体
     * 
     *  注意:主预制体释放时需要调用releaseAll(),或者与releaseRes()对应调用
     * 
     * @param path 资源路径
     * @param cb returns Script 返回预制体所挂同名脚本
     * @param root 根节点 默认为this.node
     * @param position 初始位置
     * @param onlyOne 是否一个父节点只有一个子节点(即在一个父节点下预制体只会创建一个)
     * @returns script
     */
    async loadAndCreatePrefabAsync(path: string, root?: cc.Node, position?: cc.Vec2) {
        cc.log("加载预制体")
        return new Promise((reslove) => {
            BasePrefabMgrByHall.loadRes(path, (_prefab) => {
                let sc = this.createNode(_prefab, root, position);
                cc.log("加载预制体完毕")
                reslove(sc)
            })

        })

    }

    /** 异步执行 取数据和读取预制体
     * 
     * @param getDataCb 取数据回调
     * @param path 资源路径
     * @param root 根节点 默认为this.node
     * @param position 初始位置
     * 
     * 
     * @returns [script,data]
     */
    async getDataAndLoadPrefabCreate(getDataCb, path: string, data: any, root?: cc.Node, position?: cc.Vec2) {
        let scP = this.loadAndCreatePrefabAsync(path, root, position)
        let dataP = getDataCb(data);
        return await Promise.all<any, any>([scP, dataP])

    }


    /**
     * 
     * @param path 资源路径 (如果releaseAll被调用,那么此方法可以不调)
     */
    releaseRes(path) {
        BasePrefabMgrByHall.releaseRes(path);
    }

    /**
     * 仅限主预制体onDestroy时调用
     * 可以不调用,仅用于方式释放漏
     */
    releaseAll() {
        BasePrefabMgrByHall.releaseAll();
    }

}
